package com.sinog.alarm.schedule.monitor;

import com.sinog.alarm.mapper.db1.SystemInfoMapper;
import com.sinog.alarm.util.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import oshi.SystemInfo;
import oshi.hardware.Baseboard;
import oshi.hardware.CentralProcessor;
import oshi.hardware.CentralProcessor.TickType;
import oshi.hardware.ComputerSystem;
import oshi.hardware.Display;
import oshi.hardware.Firmware;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HWDiskStore;
import oshi.hardware.HWPartition;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.hardware.PowerSource;
import oshi.hardware.Sensors;
import oshi.hardware.UsbDevice;
import oshi.software.os.FileSystem;
import oshi.software.os.NetworkParams;
import oshi.software.os.OSFileStore;
import oshi.software.os.OSProcess;
import oshi.software.os.OperatingSystem;
import oshi.software.os.OperatingSystem.ProcessSort;
import oshi.util.FormatUtil;
import oshi.util.Util;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @desc Java系统监控测试类
 * @author lzq
 * @date 2021-09-13 17:51
 */
@Slf4j
@Component
public class SystemInfoTaskSchedule {

    @Value("${local_code.code}")
    private String localCode;
    @Autowired
    private SystemInfoMapper systemInfoMapper;

    /**
     * 描述
     * @param computerSystem computerSystem
     * @param systemInfo systemInfo
     */
    private void printComputerSystem(ComputerSystem computerSystem,com.sinog.alarm.model.SystemInfo systemInfo) {
        systemInfo.setManufacturer(computerSystem.getManufacturer());
        systemInfo.setModel(computerSystem.getModel());
        systemInfo.setSerialNumber(computerSystem.getSerialNumber());
        final Firmware firmware1 = computerSystem.getFirmware();
        systemInfo.setGjManufacturer(firmware1.getManufacturer());
        systemInfo.setGjName(firmware1.getName());
        systemInfo.setGjDescription(firmware1.getDescription());
        systemInfo.setGjVersion(firmware1.getVersion());
        systemInfo.setGjReleaseDate(null == firmware1.getReleaseDate() ? "unknown" : null == firmware1.getReleaseDate() ? "unknown" : FormatUtil.formatDate(firmware1.getReleaseDate()));
        Baseboard baseboard1 = computerSystem.getBaseboard();
        systemInfo.setZxManufacturer(baseboard1.getManufacturer());
        systemInfo.setZxModel(baseboard1.getModel());
        systemInfo.setZxSerialNumber(baseboard1.getSerialNumber());
        log.info("制造商:{}",computerSystem.getManufacturer());
        log.info("型号:{}",computerSystem.getModel());
        log.info("序列号:{}",computerSystem.getSerialNumber());
        final Firmware firmware = computerSystem.getFirmware();
        log.info("固件制造商:{}",firmware.getManufacturer());
        log.info("固件名称:{}",firmware.getName());
        log.info("固件描述:{}",firmware.getDescription());
        log.info("固件版本:{}",firmware.getVersion());
        log.info("固件有效期:{}",(null == firmware.getReleaseDate() ? "unknown" : null == firmware.getReleaseDate() ? "unknown" : FormatUtil.formatDate(firmware.getReleaseDate())));
        Baseboard baseboard = computerSystem.getBaseboard();
        log.info("总线制造商:{}",baseboard.getManufacturer());
        log.info("总线型号:{}",baseboard.getModel());
        log.info("总线版本:{}",baseboard.getVersion());
        log.info("总线序列号:{}",baseboard.getSerialNumber());
    }

    /**
     * 描述
     * @param processor processor
     * @param systemInfo systemInfo
     */
    private void printProcessor(CentralProcessor processor,com.sinog.alarm.model.SystemInfo systemInfo) {
        systemInfo.setCpuPhysicalProcessorCount(processor.getPhysicalProcessorCount());
        systemInfo.setCpuLogicalProcessorCount(processor.getLogicalProcessorCount());
        systemInfo.setCpuIdentifier(processor.getIdentifier());
        systemInfo.setCpuIdentifier(processor.getProcessorID());
        log.info("cpu物理:{}",processor.getPhysicalProcessorCount() + " physical CPU(s)");
        log.info("cpu逻辑:{}",processor.getLogicalProcessorCount() + " logical CPU(s)");
        log.info("cpu标识:{}",processor.getIdentifier());
        log.info("cpu序列号:{}",processor.getProcessorID());
    }

    /**
     * 描述
     * @param memory memory
     * @param systemInfo systemInfo
     */
    private void printMemory(GlobalMemory memory,com.sinog.alarm.model.SystemInfo systemInfo) {
        systemInfo.setNcAvailable(memory.getAvailable());
        systemInfo.setNcTotal(FormatUtil.formatBytes(memory.getTotal()));
        systemInfo.setNcSwapUsed(FormatUtil.formatBytes(memory.getSwapUsed()));
        systemInfo.setNcSwapTotal(FormatUtil.formatBytes(memory.getSwapTotal()));
        log.info("已使用内存:{}",FormatUtil.formatBytes(memory.getAvailable()) + "总共内存" + FormatUtil.formatBytes(memory.getTotal()));
        log.info("Swap used:{}",FormatUtil.formatBytes(memory.getSwapUsed()) + "/" + FormatUtil.formatBytes(memory.getSwapTotal()));
    }

    /**
     * 描述
     * @param processor processor
     * @param systemInfo systemInfo
     */
    private void printCpu(CentralProcessor processor,com.sinog.alarm.model.SystemInfo systemInfo) {
        log.info("操作系统启动时间:{}" + FormatUtil.formatElapsedSecs(processor.getSystemUptime()));
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        log.info("CPU, IOWait, and IRQ ticks @ 0 sec:{}" + Arrays.toString(prevTicks));
        // Wait a second...
        Util.sleep(1000);
        long[] ticks = processor.getSystemCpuLoadTicks();
        log.info("CPU, IOWait, and IRQ ticks @ 1 sec:{}" + Arrays.toString(ticks));
        long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
        long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
        long sys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
        long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
        long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
        long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
        long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
        long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
        long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal;
        log.info("User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%%n:{}" + 100d * user / totalCpu,100d * nice / totalCpu,100d * sys / totalCpu,100d * idle / totalCpu,100d * iowait / totalCpu,100d * irq / totalCpu,100d * softirq / totalCpu,100d * steal / totalCpu);
        log.info("CPU load: %.1f%% (counting ticks)%n:{}",processor.getSystemCpuLoadBetweenTicks() * 100);
        log.info("CPU load: %.1f%% (OS MXBean)%n:{}",processor.getSystemCpuLoad() * 100);
        double[] loadAverage = processor.getSystemLoadAverage(3);
        log.info("CPU load averages:{}" + (0 > loadAverage[0] ? " N/A" : String.format(" %.2f",loadAverage[0])) + (0 > loadAverage[1] ? " N/A" : String.format(" %.2f",loadAverage[1])) + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f",loadAverage[2])));
        // per core CPU
        StringBuilder procCpu = new StringBuilder("CPU load per processor:");
        double[] load = processor.getProcessorCpuLoadBetweenTicks();
        for(double avg : load) {
            procCpu.append(String.format(" %.1f%%",avg * 100));
        }
        log.info(procCpu.toString());
        systemInfo.setXtSystemUptime(FormatUtil.formatElapsedSecs(processor.getSystemUptime()));
        systemInfo.setXtPrevTicks(Arrays.toString(processor.getSystemCpuLoadTicks()));
        systemInfo.setXtTicks(Arrays.toString(processor.getSystemCpuLoadTicks()));
        systemInfo.setXtUser(ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]);
        systemInfo.setXtNice(ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]);
        systemInfo.setXtSys(ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]);
        systemInfo.setXtIdle(ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]);
        systemInfo.setXtIoWait(ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]);
        systemInfo.setXtIrq(ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]);
        systemInfo.setXtSteal(ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]);
        systemInfo.setXtTotalCpu(totalCpu);
        systemInfo.setXtLoadAverage((0 > loadAverage[0] ? " N/A" : String.format(" %.2f",loadAverage[0])) + (0 > loadAverage[1] ? " N/A" : String.format(" %.2f",loadAverage[1])) + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f",loadAverage[2])));
        systemInfo.setXtProcCpu(procCpu.toString());
    }

    /**
     * 描述
     * @param os os
     * @param memory memory
     */
    private void printProcesses(OperatingSystem os,GlobalMemory memory) {
        log.info("进程:{}",os.getProcessCount() + ", Threads: " + os.getThreadCount());
        // Sort by highest CPU
        List<OSProcess> procs = Arrays.asList(os.getProcesses(5,ProcessSort.CPU));
        log.info("   PID  %CPU %MEM       VSZ       RSS Name");
        for(int i = 0; i < procs.size() && i < Constants.Number.NUM_FIVE; i++) {
            OSProcess p = procs.get(i);
            log.info(" %5d %5.1f %4.1f %9s %9s %s%n",p.getProcessID(),100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(),100d * p.getResidentSetSize() / memory.getTotal(),FormatUtil.formatBytes(p.getVirtualSize()),FormatUtil.formatBytes(p.getResidentSetSize()),p.getName());
        }
    }

    /**
     * 描述
     * @param sensors sensors
     */
    private void printSensors(Sensors sensors) {
        log.info("传感器:");
        System.out.format(" CPU Temperature: %.1f°C%n",sensors.getCpuTemperature());
        log.info(" Fan Speeds: " + Arrays.toString(sensors.getFanSpeeds()));
        System.out.format(" CPU Voltage: %.1fV%n",sensors.getCpuVoltage());
    }

    /**
     * 描述
     * @param powerSources powerSources
     */
    private void printPowerSources(PowerSource[] powerSources) {
        StringBuilder sb = new StringBuilder("Power: ");
        if(0 == powerSources.length) {
            sb.append("Unknown");
        } else {
            double timeRemaining = powerSources[0].getTimeRemaining();
            if(-1.0d > timeRemaining) {
                sb.append("Charging");
            } else if(0.0d > timeRemaining) {
                sb.append("Calculating time remaining");
            } else {
                sb.append(String.format("%d:%02d remaining",(int)(timeRemaining / 3600),(int)(timeRemaining / 60) % 60));
            }
        }
        for(PowerSource pSource : powerSources) {
            sb.append(String.format("%n %s @ %.1f%%",pSource.getName(),pSource.getRemainingCapacity() * 100.0d));
        }
        log.info(sb.toString());
    }

    /**
     * 磁盘不存。
     * @param diskStores diskStores
     */
    private void printDisks(HWDiskStore[] diskStores) {
        log.info("磁盘:");
        for(HWDiskStore disk : diskStores) {
            boolean readwrite = 0 < disk.getReads() || 0 < disk.getWrites();
            System.out.format(" %s: (model: %s - S/N: %s) size: %s, reads: %s (%s), writes: %s (%s), xfer: %s ms%n",disk.getName(),disk.getModel(),disk.getSerial(),0 < disk.getSize() ? FormatUtil.formatBytesDecimal(disk.getSize()) : "?",readwrite ? disk.getReads() : "?",readwrite ? FormatUtil.formatBytes(disk.getReadBytes()) : "?",readwrite ? disk.getWrites() : "?",readwrite ? FormatUtil.formatBytes(disk.getWriteBytes()) : "?",readwrite ? disk.getTransferTime() : "?");
            HWPartition[] partitions = disk.getPartitions();
            if(null == partitions) {
                continue;
            }
            for(HWPartition part : partitions) {
                System.out.format(" |-- %s: %s (%s) Maj:Min=%d:%d, size: %s%s%n",part.getIdentification(),part.getName(),part.getType(),part.getMajor(),part.getMinor(),FormatUtil.formatBytesDecimal(part.getSize()),part.getMountPoint()
                                                                                                                                                                                                                          .isEmpty() ? "" : " @ " + part.getMountPoint());
            }
        }
    }

    /**
     * 描述
     * @param fileSystem fileSystem
     * @param systemInfo systemInfo
     */
    private void printFileSystem(FileSystem fileSystem,com.sinog.alarm.model.SystemInfo systemInfo) {
        log.info("文件系统:");
        System.out.format(" File Descriptors: %d/%d%n",fileSystem.getOpenFileDescriptors(),fileSystem.getMaxFileDescriptors());
        OSFileStore[] fsArray = fileSystem.getFileStores();
        for(OSFileStore fs : fsArray) {
            long usable = fs.getUsableSpace();
            long total = fs.getTotalSpace();
            System.out.format(" %s (%s) [%s] %s of %s free (%.1f%%) is %s " + (null != fs.getLogicalVolume() && !fs.getLogicalVolume()
                                                                                                                   .isEmpty() ? "[%s]" : "%s") + " and is mounted at %s%n",fs.getName(),fs.getDescription()
                                                                                                                                                                                          .isEmpty() ? "file system" : fs.getDescription(),fs.getType(),FormatUtil.formatBytes(usable),FormatUtil.formatBytes(fs.getTotalSpace()),100d * usable / total,fs.getVolume(),fs.getLogicalVolume(),fs.getMount());
        }
        systemInfo.setWjFileDescriptors(fileSystem.getOpenFileDescriptors() + "/" + fileSystem.getMaxFileDescriptors());
    }

    /**
     * 未储存数据库
     * @param networkIfs networkIfs
     */
    private void printNetworkInterfaces(NetworkIF[] networkIfs) {
        log.info("网络接口:");
        for(NetworkIF net : networkIfs) {
            System.out.format(" Name: %s (%s)%n",net.getName(),net.getDisplayName());
            System.out.format("   MAC Address: %s %n",net.getMacaddr());
            System.out.format("   MTU: %s, Speed: %s %n",net.getMTU(),FormatUtil.formatValue(net.getSpeed(),"bps"));
            System.out.format("   IPv4: %s %n",Arrays.toString(net.getIPv4addr()));
            System.out.format("   IPv6: %s %n",Arrays.toString(net.getIPv6addr()));
            boolean hasData = 0 < net.getBytesRecv() || 0 < net.getBytesSent() || 0 < net.getPacketsRecv() || 0 < net.getPacketsSent();
            System.out.format("   Traffic: received %s/%s%s; transmitted %s/%s%s %n",hasData ? net.getPacketsRecv() + " packets" : "?",hasData ? FormatUtil.formatBytes(net.getBytesRecv()) : "?",hasData ? " (" + net.getInErrors() + " err)" : "",hasData ? net.getPacketsSent() + " packets" : "?",hasData ? FormatUtil.formatBytes(net.getBytesSent()) : "?",hasData ? " (" + net.getOutErrors() + " err)" : "");
        }
    }

    /**
     * 描述
     * @param networkParams networkParams
     * @param systemInfo systemInfo
     */
    private void printNetworkParameters(NetworkParams networkParams,com.sinog.alarm.model.SystemInfo systemInfo) {
        log.info("网络信息:");
        System.out.format(" Host name: %s%n",networkParams.getHostName());
        System.out.format(" Domain name: %s%n",networkParams.getDomainName());
        System.out.format(" DNS servers: %s%n",Arrays.toString(networkParams.getDnsServers()));
        System.out.format(" IPv4 Gateway: %s%n",networkParams.getIpv4DefaultGateway());
        System.out.format(" IPv6 Gateway: %s%n",networkParams.getIpv6DefaultGateway());
        systemInfo.setWlHostName(networkParams.getHostName());
        systemInfo.setWlDomainName(networkParams.getDomainName());
        systemInfo.setWlDnsServers(Arrays.toString(networkParams.getDnsServers()));
        systemInfo.setWlIpv4DefaultGateway(networkParams.getIpv4DefaultGateway());
        systemInfo.setWlIpv6DefaultGateway(networkParams.getIpv6DefaultGateway());
    }

    /**
     * 描述
     * @param displays displays
     */
    private void printDisplays(Display[] displays) {
        log.info("显示器:");
        int i = 0;
        for(Display display : displays) {
            log.info(" Display " + i + ":");
            log.info(display.toString());
            i++;
        }
    }

    /**
     * 描述
     * @param usbDevices usbDevices
     */
    private static void printUsbDevices(UsbDevice[] usbDevices) {
        log.info("USB:");
        for(UsbDevice usbDevice : usbDevices) {
            log.info(usbDevice.toString());
        }
    }

    /**
     * 描述
     */
    @Scheduled(cron = "${corn}")
    public void executeTaskSchedule() {
        com.sinog.alarm.model.SystemInfo systemInfo = new com.sinog.alarm.model.SystemInfo();
        systemInfoMapper.deleteByLocalCode(localCode);
        log.info("开始初始化系统...");
        SystemInfo si = new SystemInfo();
        HardwareAbstractionLayer hal = si.getHardware();
        OperatingSystem os = si.getOperatingSystem();
        log.info("操作系统:{}",os);
        log.info("正在检查服务器系统...");
        printComputerSystem(hal.getComputerSystem(),systemInfo);
        log.info("正在检查cpu...");
        printProcessor(hal.getProcessor(),systemInfo);
        log.info("正在检查内存...");
        printMemory(hal.getMemory(),systemInfo);
        log.info("正在检查CPU...");
        printCpu(hal.getProcessor(),systemInfo);
        log.info("正在检查进程...");
        printProcesses(os,hal.getMemory());
        log.info("正在检查传感器...");
        printSensors(hal.getSensors());
        log.info("Checking Power sources...");
        printPowerSources(hal.getPowerSources());
        log.info("正在检查磁盘...");
        printDisks(hal.getDiskStores());
        log.info("正在检查文件系统...");
        printFileSystem(os.getFileSystem(),systemInfo);
        log.info("正在检查网络接口...");
        printNetworkInterfaces(hal.getNetworkIFs());
        log.info("正在检查网络信息...");
        printNetworkParameters(os.getNetworkParams(),systemInfo);
        // hardware: displays
        log.info("正在检查显示器...");
        printDisplays(hal.getDisplays());
        // hardware: USB devices
        log.info("正在检查usb...");
        printUsbDevices(hal.getUsbDevices(true));
        systemInfo.setLocalCode(localCode);
        systemInfo.setCreateTime(new Date());
        systemInfo.insert();
    }
}